﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MethodWorx.Core.Random;

namespace MethodWorx.Core.Extensions
{
    public static class StringExtensions
    {
        /// <summary>
        /// Regular expression that validates Guids in string form according to spec defined as acceptable by the Guid constructor 
        /// <para></para>
        /// ALWAYS ACCESS THIS VIA THE PROPERTY 'GuidFormatRegex' TO ENSURE IT HAS BEEN INITIALISED WITH A VALUE
        /// </summary>
        [ThreadStatic]
        private static Regex guidFormatRegex;

        /// <summary>
        /// Regular expression that validates Guids in string form according to spec defined as acceptable by the Guid constructor 
        /// </summary>
        private static Regex GuidFormatRegex
        {
            get
            {
                if (guidFormatRegex == null)
                {
                    guidFormatRegex = new Regex(@"^[A-Fa-f0-9]{32}$|({|\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\))?$|^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$", RegexOptions.Singleline | RegexOptions.Compiled);
                }

                return guidFormatRegex;
            }
        }

        public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
        {
            StringBuilder sb = new StringBuilder();

            int previousIndex = 0;
            int index = str.IndexOf(oldValue, comparison);
            while (index != -1)
            {
                sb.Append(str.Substring(previousIndex, index - previousIndex));
                sb.Append(newValue);
                index += oldValue.Length;

                previousIndex = index;
                index = str.IndexOf(oldValue, index, comparison);
            }
            sb.Append(str.Substring(previousIndex));

            return sb.ToString();
        }


        /// <summary>
        /// Get a random name of the specified length, using lower case letters 'a' to 'z'.
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public static string GetRandomString(int length)
        {
            RandomNumberGenerator r = new RandomNumberGenerator();
            char[] name = new char[length];

            for (int i = 0; i < length; i++)
            {
                name[i] = (Char)r.Next(97, 122);
            }

            return new String(name);
        }

        /// <summary>
        /// Returns the first 'n' digits found in the given text
        /// </summary>
        /// <param name="text"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string GetDigits(IEnumerable<string> text, int count)
        {
            if (text == null || count < 1) return string.Empty;

            StringBuilder result = new StringBuilder(count);

            foreach (string str in text)
            {
                for (int i = 0; i < str.Length && result.Length < count; i++)
                {
                    if (char.IsDigit(str, i)) result.Append(str[i]);
                }

                if (result.Length >= count) break;
            }

            return result.ToString();
        }

        /// <summary>
        /// Returns the first 'n' digits found in the given text
        /// </summary>
        /// <param name="text"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string GetDigits(string text, int count)
        {
            if (text.IsNullOrEmpty() || count < 1) return string.Empty;

            StringBuilder result = new StringBuilder(count);

            for (int i = 0; i < text.Length && result.Length < count; i++)
            {
                if (char.IsDigit(text, i)) result.Append(text[i]);
            }

            return result.ToString();
        }

        /// <summary>
        /// Returns a string with all HTML code removed
        /// </summary>
        /// <param name="sourceString"></param>
        /// <returns></returns>
        public static string RemoveHtmlFromString(string sourceString, string replaceString)
        {
            string pattern = @"<[^<>]+>";
            return Regex.Replace(sourceString, pattern, replaceString);
        }

        /// <summary>
        /// Returns a string where words in capital have been converted to lower case
        /// </summary>
        /// <param name="sourceString"></param>
        /// <returns></returns>
        public static string ConvertUpperCaseTextToLower(string sourceString)
        {
            string pattern = @"(\b[^\Wa-z0-9_]+\b)";
            MatchCollection matches = Regex.Matches(sourceString, pattern);
            foreach (Match mat in matches)
            {
                sourceString = sourceString.Replace(mat.Value, mat.Value.ToLower());
            }
            return sourceString;
        }

        /// <summary>
        /// Convert text to sentenence case
        /// </summary>
        /// <param name="sourceString"></param>
        /// <returns></returns>
        /// 
        public static string ConvertTextToSentenceCase(string sourceString)
        {
            string output = "", letter = "";
            string[] periods = { ".", "*", "#" };   // Array of characters to be used as periods
            bool periodFound = true;                // Should default to true so that first character in string gets set to upper case

            // Convert all text to lowercase & replace double spaces
            sourceString = sourceString.ToLower();
            sourceString = sourceString.Replace("  ", " ");

            foreach (char c in sourceString)
            {
                if (!periodFound)
                {
                    foreach (string p in periods)
                    {
                        if (c.ToString() == p)
                        {
                            periodFound = true;
                            break;
                        }
                    }
                }
                letter = c.ToString();
                if (!char.IsLetter(c))
                {
                    output += letter;
                }
                else
                {
                    if (periodFound)
                    {
                        letter = letter.ToUpper();
                        periodFound = false;
                    }
                    output += letter;
                }
            }

            return output;
        }

        /// <summary>
        /// Converts the first character of the string to upper-case
        /// </summary>
        /// <param name="sourceString"></param>
        /// <returns></returns>
        public static string FirstCharToUpper(this string sourceString)
        {
            if (!string.IsNullOrEmpty(sourceString))
            {
                if (sourceString.Length > 1)
                {
                    sourceString = sourceString.Substring(0, 1).ToUpper() + sourceString.Substring(1, sourceString.Length - 1);
                }
                else
                {
                    sourceString = sourceString.ToUpper();
                }
            }

            return sourceString;
        }

        /// <summary>
        /// Converts an array of objects and turns them into a string array list represented by
        /// a ',' delimeter.  Objects have their .ToString() method called.
        /// Return example, "1,2,3,4" 
        /// </summary>
        /// <param name="array">An array of objects</param>
        /// <returns>A string represented array</returns>
        public static string ArrayToString(object[] array)
        {
            StringBuilder returnVal = new StringBuilder();

            foreach (object o in array)
            {
                returnVal.Append("," + o.ToString());
            }

            if (returnVal.Length > 0)
                return returnVal.ToString().Remove(0, 1);
            else
                return returnVal.ToString();
        }

        public static string AddSpacesToString(string input)
        {
            //if the whole string is in capital letters 
            //we assume that this is abbreviation and will not change it 

            if (input.ToUpper() == input)
                return input;

            string output = string.Empty;

            input = input.Replace("_____", ".");
            input = input.Replace("____", "(");
            input = input.Replace("___", ")");
            input = input.Replace("__", "/");
            input = input.Replace("_", ",");
            char[] buffer = input.ToCharArray();

            for (int i = 0; i < buffer.Length; i++)
            {
                char c = buffer[i];


                bool isChangeOfCase = char.IsUpper(c) || char.IsNumber(c);
                bool isNextChangeOfCase = false;

                if (i < buffer.Length - 1)
                {
                    char next = buffer[i + 1];
                    isNextChangeOfCase = char.IsUpper(next) || char.IsNumber(next);
                }

                if (isChangeOfCase && !isNextChangeOfCase)
                    output += " ";

                output += new String(c, 1);
            }

            return output.TrimStart();
        }

        /// <summary>
        /// Returns the first "quoted" element from a string
        /// </summary>
        /// <param name="input"></param>
        /// <param name="quotedIdentifier"></param>
        /// <param name="escapeChar"></param>
        /// <param name="seperator"></param>
        /// <returns></returns>
        public static string StripFirstQuotedString(string input, char? quotedIdentifier, char? escapeChar, char? seperator)
        {
            if (string.IsNullOrEmpty(input))
                throw new System.ArgumentNullException("input");

            List<char> output = new List<char>();

            int charIndex = 0;
            bool foundEscape = false;
            bool inString = false;
            foreach (char c in input)
            {
                if (quotedIdentifier.HasValue && charIndex == 0 && c != quotedIdentifier.Value)
                    break;  //  is the first character the quoted identifier, if not we must escape
                else if (quotedIdentifier.HasValue || charIndex > 0)
                {
                    if (quotedIdentifier.HasValue)
                        inString = true;

                    if (escapeChar != null && foundEscape == false && c == escapeChar)
                        foundEscape = true;
                    else
                    {
                        if (quotedIdentifier.HasValue && c == quotedIdentifier.Value && foundEscape == false)
                            break;
                        else
                        {
                            if (inString == false && seperator != null && c == seperator && foundEscape == false)
                                break;
                            else
                            {
                                output.Add(c);
                                foundEscape = false;
                            }
                        }
                    }
                }

                ++charIndex;
            }

            //  get the string
            return new string(output.ToArray());
        }


        /// <summary>
        /// Parses a function element out of a string eg FUNCTION(THIS IS A FUNCTION)
        /// </summary>
        /// <param name="text"></param>
        /// <param name="functionElement"></param>
        /// <returns></returns>
        public static string ParseFunctionElement(string text, string functionElement)
        {
            if (string.IsNullOrEmpty(text))
                throw new System.ArgumentNullException("text");

            if (string.IsNullOrEmpty(functionElement))
                throw new System.ArgumentNullException("functionElement");

            //  get the start
            int start = text.ToUpper().IndexOf(functionElement.ToUpper());
            int bracketCount = 0;

            //  next find the first bracket
            for (int i = start + functionElement.Length; i < text.Length; i++)
            {
                char c = text[i];
                if (char.IsWhiteSpace(c))
                    continue;
                else
                {
                    if (c == '(')
                        bracketCount++;
                    else if (c == ')')
                    {
                        bracketCount--;
                        //  at the last braket, so return
                        if (bracketCount == 0)
                            return text.Substring(start, (i - start) + 1);    //  function has not failed
                    }
                    else if (bracketCount == 0)
                        break;
                }
            }

            //  function has failed now
            return null;
        }

        /// <summary>
        /// Parse the function arguments out of the function eg FUNCTION ("ARGUMENT")
        /// </summary>
        /// <param name="minLength"></param>
        /// <param name="maxLength"></param>
        /// <returns></returns>
        public static string[] ParseFunctionArguments(int minLength, int maxLength)
        {
            return null;
        }

        /// <summary>
        /// Parse the single and only function argument out of the function
        /// </summary>
        /// <param name="function"></param>
        /// <returns></returns>
        public static string ParseFunctionArgument(string function)
        {
            if (string.IsNullOrEmpty(function))
                throw new System.ArgumentNullException("function");

            //  get the start
            int start = function.ToUpper().Trim().IndexOf('(');
            int end = function.ToUpper().Trim().LastIndexOf(')');

            //  function has failed now
            return start >= 0 && end > 0 ? function.Substring(start + 1, end - start - 1) : string.Empty;
        }

        /// <summary>
        /// Parse the required amount of words out of string
        /// </summary>
        /// <param name="text"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static string[] ParseWords(string text, int count)
        {
            if (string.IsNullOrEmpty(text))
                throw new ArgumentNullException("text");

            if (count <= 0)
                throw new ArgumentOutOfRangeException("count", "the number of words must be greater than zero");

            string[] words = new string[count];
            int index = 0;
            string word = string.Empty;

            foreach (char c in text)
            {
                if (!char.IsWhiteSpace(c))
                    word += c;
                else
                {
                    words[index] = word;
                    index++;
                    word = string.Empty;
                    // we've got all the words we want, so break out of the loop to ensure the
                    // array index boundary is not exceeded
                    if (index == count)
                        break;
                }
            }

            // add the last word if it hasn't already been added
            if (word != string.Empty)
                words[index] = word;

            return words;
        }

        /// <summary>
        /// Parse the first word out of the string
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string ParseFirstWord(string text)
        {
            string[] words = ParseWords(text, 1);
            return words.Length > 0 ? words[0] : string.Empty;
        }

        /// <summary>
        /// Joins the given set of strings, inserting the given separator between each string
        /// </summary>
        /// <param name="separator">The character(s) to insert between each string</param>
        /// <param name="strings">The set of strings to join</param>
        /// <returns></returns>
        public static string Join(string separator, params string[] strings)
        {
            return Join(separator, false, strings);
        }

        /// <summary>
        /// Joins the given set of strings, inserting the given separator between each string
        /// </summary>
        /// <param name="separator">The character(s) to insert between each string</param>
        /// <param name="excludeEmptyStrings">Indicates whether null/empty strings should be included in the result</param>
        /// <param name="strings">The set of strings to join</param>
        /// <returns></returns>
        public static string Join(string separator, bool excludeEmptyStrings, params string[] strings)
        {
            if (strings.Length == 0) return null;

            StringBuilder result = new StringBuilder();

            foreach (string s in strings)
            {
                if (excludeEmptyStrings)
                {
                    if (!s.IsNullOrEmpty())
                    {
                        result.Append(s);
                        result.Append(separator);
                    }
                }
                else
                {
                    result.Append(s);
                    result.Append(separator);
                }
            }

            if (result.Length > 0)
            {
                result.Remove(result.Length - separator.Length, separator.Length);
            }

            return result.ToString();
        }

        /// <summary>
        /// Replace, using regular expressions, the input string value with a new value
        /// </summary>
        /// <param name="input">The strng to search for a match</param>
        /// <param name="pattern">The regular expression pattern to match</param>
        /// <param name="replacement">The replacement value to be substituted where matches are found</param>
        /// <param name="caseSensitive">Indicates whether matching should be case-sensitive</param>
        public static string Replace(this string input, string pattern, string replacement, bool caseSensitive)
        {
            return Regex.Replace(input, pattern, replacement, (caseSensitive) ? RegexOptions.None : RegexOptions.IgnoreCase);
        }

        /// <summary>
        /// Converts the string into proper case
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string CapitalizeWords(this string input)
        {
            // ToTitleCase only converts the first character, so convert to lower case first, incase the word(s) 
            // are capitalised already
            return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input.ToLower());
        }

        /// <summary>
        /// Reverses the order of the characters in the string
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string Reverse(this string input)
        {
            char[] chars = input.ToCharArray();
            Array.Reverse(chars);
            return new String(chars);
        }

        /// <summary>
        /// Returns the percentage of characters that are uppercase in a string 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static double PercentageUppercase(this string input)
        {
            if (string.IsNullOrEmpty(input)) return 0.0;

            int uppercaseCount = 0;

            for (int i = 0; i < input.Length; i++)
            {
                if (char.IsUpper(input, i))
                {
                    uppercaseCount++;
                }
            }

            return ((double)uppercaseCount / (double)input.Length) * 100.0;
        }

        /// <summary>
        /// Indicates whether a string is null or empty
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string input)
        {
            return (input == null || input.Length == 0);
        }

        /// <summary>
        /// Indicates whether a string is null or empty or contains only whitespace characters
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsNullOrEmptyOrWhiteSpace(this string input)
        {
            if (input == null || input.Length == 0) return true;

            for (int i = 0; i < input.Length; i++)
            {
                if (!char.IsWhiteSpace(input, i))
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Calculates the Levenshtein distance between two strings, which is defined as the minimum number of 'operations'
        /// needed to transform one string into the other, where an 'operation' is an insertion, deletion, or substitution 
        /// of a single character.
        /// <para></para>
        /// This distance can be used to infer the similarity of two strings.
        /// </summary>
        /// <param name="s">The first string</param>
        /// <param name="t">The second string</param>
        /// <returns>The number of operations required to transform the first string into the second string</returns>
        public static int LevenshteinDistance(string s, string t)
        {
            return LevenshteinDistance(s, t, false);
        }

        /// <summary>
        /// Calculates the Levenshtein distance between two strings, which is defined as the minimum number of 'operations'
        /// needed to transform one string into the other, where an 'operation' is an insertion, deletion, or substitution 
        /// of a single character.
        /// <para></para>
        /// This distance can be used to infer the similarity of two strings.
        /// </summary>
        /// <param name="s">The first string</param>
        /// <param name="t">The second string</param>
        /// <param name="ignoreCase">Indicates whether differences in case should be ignored</param>
        /// <returns>The number of operations required to transform the first string into the second string</returns>
        public static int LevenshteinDistance(string s, string t, bool ignoreCase)
        {
            int n = (s == null) ? 0 : s.Length;
            int m = (t == null) ? 0 : t.Length;

            // check for empty strings
            if (n == 0) return m;
            if (m == 0) return n;

            int[,] d = new int[n + 1, m + 1];

            // initialise first items in the matrix
            for (int i = 0; i <= n; i++) { d[i, 0] = i; }
            for (int j = 0; j <= m; j++) { d[0, j] = j; }

            // calculate matrix values...
            for (int i = 1; i <= n; i++)
            {
                char sChar = s[i - 1];

                for (int j = 1; j <= m; j++)
                {
                    char tChar = t[j - 1];

                    int cost = (sChar == tChar ? 0 : 1);

                    d[i, j] = System.Math.Min(System.Math.Min(
                                d[i - 1, j] + 1,
                                d[i, j - 1] + 1),
                                d[i - 1, j - 1] + cost
                              );
                }
            }

            // return calculated 'edit distance'
            return d[n, m];
        }

        /// <summary>
        /// Calculates the Damerau-Levenshtein distance between two strings, which is defined as the minimum number of 'operations'
        /// needed to transform one string into the other, where an 'operation' is an insertion, deletion, or substitution 
        /// of a single character, <i>or the transposition of two adjacent characters</i>.
        /// <para></para>
        /// This distance can be used to infer the similarity of two strings.
        /// </summary>
        /// <param name="s">The first string</param>
        /// <param name="t">The second string</param>
        /// <returns>The number of operations required to transform the first string into the second string</returns>
        public static int DamerauLevenshteinDistance(string s, string t)
        {
            return DamerauLevenshteinDistance(s, t, false);
        }

        /// <summary>
        /// Calculates the Damerau-Levenshtein distance between two strings, which is defined as the minimum number of 'operations'
        /// needed to transform one string into the other, where an 'operation' is an insertion, deletion, or substitution 
        /// of a single character, <i>or the transposition of two adjacent characters</i>.
        /// <para></para>
        /// This distance can be used to infer the similarity of two strings.
        /// </summary>
        /// <param name="s">The first string</param>
        /// <param name="t">The second string</param>
        /// <param name="ignoreCase">Indicates whether differences in case should be ignored</param>
        /// <returns>The number of operations required to transform the first string into the second string</returns>
        public static int DamerauLevenshteinDistance(string s, string t, bool ignoreCase)
        {
            int n = (s == null) ? 0 : s.Length;
            int m = (t == null) ? 0 : t.Length;

            // check for empty strings
            if (n == 0) return m;
            if (m == 0) return n;

            int[,] d = new int[n + 1, m + 1];

            // initialise first items in the matrix
            for (int i = 0; i <= n; i++) { d[i, 0] = i; }
            for (int j = 0; j <= m; j++) { d[0, j] = j; }

            // calculate matrix values...
            for (int i = 1; i <= n; i++)
            {
                char sChar = s[i - 1];

                for (int j = 1; j <= m; j++)
                {
                    char tChar = t[j - 1];

                    int cost = (sChar == tChar ? 0 : 1);

                    d[i, j] = System.Math.Min(System.Math.Min(
                                d[i - 1, j] + 1,
                                d[i, j - 1] + 1),
                                d[i - 1, j - 1] + cost
                              );

                    if (i > 1 && j > 1 && sChar == t[j - 2] && s[i - 2] == tChar)
                    {
                        d[i, j] = System.Math.Min(
                                    d[i, j],
                                    d[i - 2, j - 2] + cost   // transposition
                                  );
                    }
                }
            }

            // return calculated 'edit distance'
            return d[n, m];
        }

        #region Type Checks and Conversions

        /// <summary>
        /// Indicates whether the string can be parsed as a valid boolean value
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsBoolean(this string input)
        {
            bool dummy;
            return bool.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid 8-bit unsigned integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsByte(this string input)
        {
            byte dummy;
            return byte.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid 16-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsInt16(this string input)
        {
            short dummy;
            return short.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid 32-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsInt32(this string input)
        {
            int dummy;
            return int.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid 64-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsInt64(this string input)
        {
            long dummy;
            return long.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid 32-bit floating point number
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsSingle(this string input)
        {
            float dummy;
            return float.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid 64-bit floating point number
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsDouble(this string input)
        {
            double dummy;
            return double.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid decimal number
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsDecimal(this string input)
        {
            decimal dummy;
            return decimal.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid DateTime
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsDateTime(this string input)
        {
            DateTime dummy;
            return DateTime.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid DateTime, based on the given format string
        /// </summary>
        /// <param name="input"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static bool IsDateTime(this string input, string format)
        {
            DateTime dummy;
            return DateTime.TryParseExact(input, format, null, System.Globalization.DateTimeStyles.None, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid TimeSpan
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsTimeSpan(this string input)
        {
            TimeSpan dummy;
            return TimeSpan.TryParse(input, out dummy);
        }

        /// <summary>
        /// Indicates whether the string can be parsed as a valid Guid
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool IsGuid(this string input)
        {
            if (input == null) return false;
            return GuidFormatRegex.IsMatch(input);
        }

        /// <summary>
        /// Attempts to convert the string to a boolean value
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool ToBoolean(this string input)
        {
            return bool.Parse(input);
        }

        /// <summary>
        /// Attempts to convert the string to a boolean value
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToBoolean(this string input, out bool result)
        {
            return bool.TryParse(input, out result);
        }

        /// <summary>
        /// Return a bool value from the string, or a NULL if it can not parse it
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static bool? ToNullableBoolean(this string input)
        {
            if (input != null && input.IsBoolean())
                return input.ToBoolean();
            else
                return null;
        }

        /// <summary>
        /// Attempts to convert the string to an 8-bit unsigned integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static byte ToByte(this string input)
        {
            return byte.Parse(input);
        }

        /// <summary>
        /// Attempts to convert the string to an 8-bit unsigned integer
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToByte(this string input, out byte result)
        {
            return byte.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a 16-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static short ToInt16(this string input)
        {
            return short.Parse(input);
        }

        /// <summary>
        /// Attempts to convert the string to a 16-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToInt16(this string input, out short result)
        {
            return short.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a 32-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static int ToInt32(this string input)
        {
            return int.Parse(input);
        }

        public static int? ToNullableInt32(this string input)
        {
            if (input != null && input.IsInt32())
                return input.ToInt32();
            else
                return null;
        }

        /// <summary>
        /// Attempts to convert the string to a 32-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToInt32(this string input, out int result)
        {
            return int.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a 64-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToInt64(this string input, out long result)
        {
            return long.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a 64-bit integer
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static long ToInt64(this string input)
        {
            return long.Parse(input);
        }

        /// <summary>
        /// Return a long value from the string, or a NULL if it can not parse it
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static long? ToNullableInt64(this string input)
        {
            if (input != null && input.IsInt64())
                return input.ToInt64();
            else
                return null;
        }

        /// <summary>
        /// Return a decimal value from the string, or a NULL if it can not parse it
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static decimal? ToNullableDecimal(this string input)
        {
            if (input != null && input.IsDecimal())
                return input.ToDecimal();
            else
                return null;
        }

        /// <summary>
        /// Attempts to convert the string to a 32-bit floating point number
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static float ToSingle(this string input)
        {
            return float.Parse(input);
        }

        /// <summary>
        /// Attempts to convert the string to a 32-bit floating point number
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToSingle(this string input, out float result)
        {
            return float.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a 64-bit floating point number
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static double ToDouble(this string input)
        {
            return double.Parse(input);
        }

        /// <summary>
        /// Attempts to convert the string to a 64-bit floating point number
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToDouble(this string input, out double result)
        {
            return double.TryParse(input, out result);
        }

        /// <summary>
        /// Return a double value from the string, or a NULL if it can not parse it
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static double? ToNullableDouble(this string input)
        {
            if (input != null && input.IsDouble())
                return input.ToDouble();
            else
                return null;
        }

        /// <summary>
        /// Attempts to convert the string to a decimal number
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static decimal ToDecimal(this string input)
        {
            return decimal.Parse(input);
        }

        /// <summary>
        /// Attempts to convert the string to a decimal number
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToSingle(this string input, out decimal result)
        {
            return decimal.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a DateTime
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(this string input)
        {
            return DateTime.Parse(input);
        }

        /// <summary>
        /// Return a bool value from the string, or a NULL if it can not parse it
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static DateTime? ToNullableDateTime(this string input)
        {
            if (input != null && input.IsDateTime())
                return input.ToDateTime();
            else
                return null;
        }

        /// <summary>
        /// Attempts to convert the string to a DateTime based on the specified format
        /// </summary>
        /// <param name="input"></param>
        /// <param name="format"></param>
        /// <returns></returns>
        public static DateTime ToDateTime(this string input, string format)
        {
            return DateTime.ParseExact(input, format, null);
        }

        /// <summary>
        /// Attempts to convert the string to a DateTime
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToDateTime(this string input, out DateTime result)
        {
            return DateTime.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a DateTime based on the specified format
        /// </summary>
        /// <param name="input"></param>
        /// <param name="format"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToDateTime(this string input, string format, out DateTime result)
        {
            return DateTime.TryParseExact(input, format, null, System.Globalization.DateTimeStyles.None, out result);
        }

        public static T ToEnum<T>(this string input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            return (T)Enum.Parse(typeof(T), input);
        }

        public static Nullable<T> ToNullableEnum<T>(this string input) where T : struct
        {
            if (input == null)
                return null;

            try
            {
                T e = (T)Enum.Parse(typeof(T), input);
                if (e.IsValidEnumMember())
                    return (Nullable<T>)e;
                else
                    return null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Attempts to convert the string to a TimeSpan
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static TimeSpan ToTimeSpan(this string input)
        {
            return TimeSpan.Parse(input);
        }

        /// <summary>
        /// Attempts to convert the string to a TimeSpan
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToTimeSpan(this string input, out TimeSpan result)
        {
            return TimeSpan.TryParse(input, out result);
        }

        /// <summary>
        /// Attempts to convert the string to a Guid.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static Guid ToGuid(this string input)
        {
            return new Guid(input);
        }


        /// <summary>
        /// Attempts to convert the string to a Guid.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static Guid? ToNullableGuid(this string input)
        {
            if (input == null)
                return null;

            try
            {
                return new Guid(input);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Attempts to convert the string to a Guid.
        /// </summary>
        /// <param name="input"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public static bool ToGuid(this string input, out Guid result)
        {
            if (input != null && GuidFormatRegex.IsMatch(input))
            {
                result = new Guid(input);
                return true;
            }
            else
            {
                result = Guid.Empty;
                return false;
            }
        }

        #endregion

    /*    public static StringList ToStringList(this string input)
        {
            StringList stringList = new StringList();

            foreach (string item in input.Split('&'))
            {
                stringList.Add(System.Web.HttpUtility.UrlDecode(item));
            }

            return stringList;
        }*/

        public static bool IsEmailAddress(this string input)
        {
            if (input == null)
                return false;

            Regex regex = new Regex(@"([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})");

            return regex.IsMatch(input);
        }

        public static bool Matches(this string input, string pattern)
        {
            return new Regex(pattern).IsMatch(input);
        }
    }
}
